Ana içeriğe geç

Enums

Enum yapıları nedir diye baktığımızda bizleri çevirisi olan sınıflandırmak karşılamaktadır. Yapı olarak bool değerlerine benzemektedirler. Belirli bir durumun sadece belirli sayıda durumunun olduğunu ifade etmektedirler. Mesela diyelim ki bir oyun yazıyoruz ve oyuncunun oyun içerisinde hangi durumda olduğunu kod içerisinde belirtmek istiyoruz. Bu durumda enum ile oyuncunun durumunu OYUNDA , ,OYUNU DURDURDU ve OYUNU KAYBETTİ durumlarından biri ile kaydedebiliriz. Bu şekilde sonrasında oluşturduğumuz enum değerini çağırarak eğer oyun durumu OYUNDA ise şu işlemi yap ifadesini kullanabiliriz.

Enumları nasıl tanımlayacağımıza baktığımızda ilk olarak enum anahtar kelimesini kullanmamız gerektiği karşımıza çıkmaktadır. Bu yapıyı fonksiyonların dışına tanımlayıp içerisine başlıkları girebiliriz.

enum EvcilHayvan {kopek, kedi, balık}

Sonrasında oluşturduğumuz bu yapıdan herhangi bir değere erişmek istediğimizde şu yapıyı kullanabilriz:

fn main(){
let dog = EvcilHayvan::kopek;
}

Aynı şekilde enum yapılarına method eklemek struct yapılarında olduğu gibi mümkündür.

impl EvcilHayvan{
fn ben_neyim(self) -> &'static str {
match self {
EvcilHayvan::kopek -> "Haw Haw",
EvcilHayvan::kedi -> "Miyaw Miyaw",
EvcilHayvan::balık -> "Gulu Gulu",
}
}
}

Üst kısımda bulunan kod karışık gelebilir bu nedenle sıra sıra inceleyelim. İlk olarak impl yapısı ile EvcilHayvan enumuna method ekleyeceğimizi ifade ederiz. Sonrasında altına indiğimizde fn yapısı ile methodumuzun ismini tanımlarız. Burada dikkat etmemiz gereken birkaç şey bulunmaktadır. İlk olarak struct yapılarına benzemeyen şekilde self yapısı referans değil kendisi olmaktadır. Sonrasında ise static lifetime ifadesi vardır. Bu durumun nedeni bir string slice döndürecek ifademizin bu işlemi yapabilmesini sağlamaktır. Sonrasında ise bir match yapısı ile her bir olasılık için Bir &str yapısı döndürülmektedir ki bunlara daha sonraki derslerimizde daha ayrıntılı olarak bakacağız.

Sonrasında aynı bir struct yapısı gibi istediğimiz yerde bu methodu çağırabilir ve istediğimiz işlemi yapıp yapmadığını kontrol edebiliriz.

fn main(){
let dog = EvcilHayvan::kopek;
println!("{}", dog.ben_neyim) // Haw Haw
}

Aynı zamanda enumları struct yapıları içerisinde de kontrol edebiliriz. Hadi bunu nasıl yapacağımıza bakalım:

enum IpAddrKind{
V4,
V6
}

struct IpAddr {
kind: IpAddrKind,
address: String
}

Hadi yukarıda yazdığımız kodu biraz daha inceleyelim. İlk olarak bir enum tanımlamışız ve içerisine V4 ve V6 değerlerini girmişiz. Daha önceden de bahsettiğimiz üzere enum yapıları belirli olasılıkları işartlendirmeye yarıyordu. Sonrasında bir struct yapısı oluşturup kind olarak oluşturduğumuz bu enum değerini girmişiz. Yani yaptığımız şey V4 ve V6 değerlerinden başka birşey olamayacak IP türümüzü String gibi büyük bir kümeden almak yerine oluşturduğumuz enum değerlerinden birini alarak güvenliği sağlamaktır. Sonrasında bu struct yapısında bir değişken oluşturabiliriz.

fn main(){
let dog = EvcilHayvan::kopek;
println!("{}", dog.ben_neyim) // Haw Haw

let ev = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1")
};
let loopack = IpAddr{
kind: IpAddrKind::V6,
address: String::from("::1")
}
}

Her nasıl struct yapıları içerisinde enum değerleri tutabiliyorsak enum yapıları içeisinde de struct yapıları tutabiliriz. Bu işlemi şu şekilde küçük modifikasyonlar ile gerçekleştirebiliriz:

enum IpAddrKind{
V4(String),
V6
}
let home = IpAddrKind::V4(String::from("127.0.0.1"))

Bu sayede direkt olarak hem türü hemde değeri enum içerisinde tanımlayabiliriz.

Önceden Tanımlanmış İşe Yarar Enum'lar

Option Enum

Option enum değeri bizim için çok önemli olmaktadır. bu durumun nedeni bir değerin yani bir şey olmasını sağlamaktadır. Ya da hiçbir şey. Bu bağlamda bulunan nothing konsepti diğer dillerdeki null ifadesine benzetilebilir. Rust içerisinde null ifadesini barındırmaz. Bunun yerine option enum içerisindeki none ifadesini kullanır.

Hadi bunları nasıl tanımlayacağımızı öğrenelim.

enum Option<T>{
None,
Some(T),
}

Yukarıdaki ifadeye baktığımızda ilk olarak enum ifademizin yanındaki <T> yapısı dikkatimizi çekebilir. Bu yapı generic type olarak adlandıracağımız bir yapı olmaktadır ve bir sonraki ana başlıkta incelenecektir. Sonrasında değer olarak None ve Some ifadesi vardır. Some ifadesi içerisinde bulunan struct sayesinde işlem yapılabilir. Bu işlem içesinde herhangi bir tipi barındırabilir.

Bu tanımları çalıştırmak istediğimizde şu şekilde bir yapı oluşturabiliriz:

let herhangi_sayi: Option<i32> = Some(5);
let herhangi_yazi = Some("a string");
let nothing: Option<i32> = None;

Dikkat etmemiz gereken şey burada kullandığımız ve Option türü ile tanıttığımız değişkenlerin normal değerler ile aynı tür olmadığıdır. Yani bir option i32 değeri ile normal i32 değerini toplayamayız.

let x:i32 = 5;
let y: Option<i32> = 5;

let sum = x + y; // error

Option ifadeleri bir durumda null ifadesi çıkabilecekse kullanmamız gereken ifadeler olmaktadır. Kodumuzun herhangi bir şeyi veya hiçbir şeyi halledebilmesini istemekteyiz. Şanlıyız ki bu durumları kontrol edebilmek için Rust içerisinde match işlemleri bulunmata!